#include "BaseShaderProgram.h"
#include "base.h"
#include "Constant.h"
#include "Utils.h"
#include <iostream>
#include <fstream>
#include <istream>
#include <string> 
#include <boost/filesystem.hpp>
using namespace std;
BaseShaderProgram::BaseShaderProgram(void)
{
	m_nProgramName = 0;
	m_validator = boost::shared_ptr<int>(new int(SHADER_NULL));
}
BaseShaderProgram::~BaseShaderProgram(void)
{
	Unload();
}
bool BaseShaderProgram::IsValid()
{
	if((*m_validator == SHADER_VALID))
		return true;
	return false;
}
bool BaseShaderProgram::Initialize(string strVSFile, string strFSFile)
{
	Unload();
	uint nVertexName = 0;
	uint nFragmentName = 0;

	string appRoot = boost::filesystem::current_path().string();
	string shaderRoot = appRoot + "\\" + PATH_SHADER;

	//Load shaders
	if(!LoadShader(shaderRoot+strVSFile, GL_VERTEX_SHADER, &nVertexName))
	{
		(*m_validator) = SHADER_ERROR;
		return false;
	}
	if(!LoadShader(shaderRoot+strFSFile, GL_FRAGMENT_SHADER, &nFragmentName))
	{
		(*m_validator) = SHADER_ERROR;
		return false;
	}

	//Link shader program
	m_nProgramName = glCreateProgram();
	// Attach vertex shader
	glAttachShader(m_nProgramName, nVertexName); 
	// Attach fragment shader
	glAttachShader(m_nProgramName, nFragmentName);
	// Link shader program
	glLinkProgram(m_nProgramName);

	//Get linking result
	int linkStatus = 0;
	glGetProgramiv(m_nProgramName, GL_LINK_STATUS, &linkStatus);
	if(linkStatus != GL_TRUE)
	{
		//Get shader linking log
		int logLength = 0;
		glGetProgramiv(m_nProgramName, GL_INFO_LOG_LENGTH, &logLength);
		int receivedLogLength = 0;
		char *strBuff = new char[logLength];
		glGetProgramInfoLog(m_nProgramName, logLength, &receivedLogLength, strBuff);

		//Create log string and deallocate memory for log message
		string strInfoLog(strBuff);
		ADelete(strBuff);
		
		//Print shader error
		ShaderError(cout << "Failed shader linking for [" <<
			GetUniqueShaderName() << "]; " << strInfoLog);

		(*m_validator) = SHADER_LINKERROR;
		return false;
	}

	(*m_validator) = SHADER_VALID;
	return true;
}
void BaseShaderProgram::DumpErrorLog()
{
	//Get shader linking log
	int logLength = 0;
	glGetProgramiv(m_nProgramName, GL_INFO_LOG_LENGTH, &logLength);
	int receivedLogLength = 0;
	char *strBuff = new char[logLength];
	glGetProgramInfoLog(m_nProgramName, logLength, &receivedLogLength, strBuff);

	//Create log string and deallocate memory for log message
	string strInfoLog(strBuff);
	ADelete(strBuff);
		
	//Print shader error
	ShaderError(cout << "Shader Log for [" <<
		GetUniqueShaderName() << "]; " << strInfoLog);
}
void BaseShaderProgram::Bind()
{
	glUseProgram(m_nProgramName);
	if(glErrorCheck())
	{
		DumpErrorLog();
		DebugBreak();
	}
}
void BaseShaderProgram::Unbind()
{
	glUseProgram(NULL);
}
void BaseShaderProgram::Unload()
{
	if((*m_validator == SHADER_LINKERROR) || (*m_validator == SHADER_VALID))
	{
		glDeleteProgram(m_nProgramName);
		(*m_validator) = SHADER_UNLOADED;
	}
}

bool BaseShaderProgram::LoadShader(string strFile, uint shaderType, uint *nIDout)
{
	if(nIDout == NULL)
		return false;
		
	ifstream file(strFile.c_str());
	if(!file)
	{
		ShaderError(cout << "Failed to open shader file: " << strFile);
		return false;
	}
		
	//Read string from file
	string src(istreambuf_iterator<char>(file), (istreambuf_iterator<char>()));
	(*nIDout) = glCreateShader(shaderType);
	
	//Copy shader source code
	const char *strPtr = src.c_str();
	glShaderSource((*nIDout), 1, &strPtr, NULL);
	
	glCompileShader(*nIDout);
	
	int shaderCompiled = 0;
	glGetShaderiv(*nIDout, GL_COMPILE_STATUS, &shaderCompiled);
	if(shaderCompiled != GL_TRUE)
	{
		//Get name for shader type
		string shaderTypeStr;
		if(shaderType == GL_VERTEX_SHADER)
			shaderTypeStr = "GL_VERTEX_SHADER";
		else if(shaderType == GL_FRAGMENT_SHADER)
			shaderTypeStr = "GL_FRAGMENT_SHADER";
		else if(shaderType == GL_GEOMETRY_SHADER)
			shaderTypeStr = "GL_GEOMETRY_SHADER";
		
		//Get shader compilation log
		int logLength = 0;
		glGetShaderiv(*nIDout, GL_INFO_LOG_LENGTH, &logLength);
		int receivedLogLength = 0;
		char *strBuff = new char[logLength];
		glGetShaderInfoLog(*nIDout, logLength, &receivedLogLength, strBuff);

		//Create log string and deallocate memory for log message
		string strInfoLog(strBuff);
		ADelete(strBuff);
		
		//Print shader error
		ShaderError(cout << "Failed shader compilation for [" <<
			GetUniqueShaderName() << "]-" << shaderTypeStr << 
			"; " << strInfoLog);
	}
	
	return true;
}